﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;

namespace Rappelz.GameServer.Database
{
    class DbDataReaderTypeCastHelper
    {
        private DbDataReader reader = null;

        public DbDataReaderTypeCastHelper(DbDataReader r)
        {
            if(r == null) throw new System.ArgumentNullException("Can't instanciate a DbDataReaderTypeCastHelper object with null DbDataReader value");
            reader = r;
        }

        private void logCastError(Exception e, int column)
        {
            string valueAsString;
            string fullStackTrace = Environment.StackTrace;

            //remove the first 4 line of the stacktrace which are :
            //at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
            //at System.Environment.get_StackTrace()
            //at Rappelz.GameServer.Database.DbDataReaderTypeCastHelper.logCastError(Exception e, Int32 column) [file name & line number]
            //at Rappelz.GameServer.Database.DbDataReaderTypeCastHelper.<get function name>(Int32 column) [file name & line number of logCastError call]
            //this way we append this modified stacktrace with the provided stacktrace (e) and we have the full stacktrace
            int pos, i;
            for (pos = -1, i = 0; i < 4 && (i == 0 || pos != -1); ++i) //(i == 0 || pos != -1) cause stop if pos == -1 and i != 0
                pos = fullStackTrace.IndexOf(Environment.NewLine, pos + 1);
            
            if (pos != -1)
                fullStackTrace = fullStackTrace.Remove(0, pos + 1);


            try
            {
                valueAsString = reader.GetString(column);
            }
            catch
            {
                valueAsString = "<unable to retreive value>";
            }
            
            //Don't log stacktrace if not in debug mode, stacktrace is somewhat long
            if(Globals.Log.Level >= XLog.Levels.Debug)
                Globals.Log.Error("Can't handle column value, SQL Column type: \"{0}\", Data string: \"{1}\"" + Environment.NewLine + "Original Stacktrace: " + e.ToString() + Environment.NewLine + fullStackTrace, reader.GetFieldType(column).ToString(), valueAsString);
            else Globals.Log.Error("Can't handle column value, SQL Column type: \"{0}\", Data string: \"{1}\"", reader.GetFieldType(column).ToString(), valueAsString);
        }

        public bool Read()
        {
            return reader.Read();
        }

        public byte GetByte(int column)
        {
            try
            {
                if (typeof(byte).IsAssignableFrom(reader.GetFieldType(column)))
                    return reader.GetByte(column);
                else if (typeof(bool).IsAssignableFrom(reader.GetFieldType(column)))
                    return (reader.GetBoolean(column) == true)? (byte)1 : (byte)0;
                else return byte.Parse(reader.GetString(column));
            }
            catch (Exception e)
            {
                logCastError(e, column);
                return (byte)0;
            }
        }

        public byte GetByte(int column, byte def)
        {
            try
            {
                if (typeof(byte).IsAssignableFrom(reader.GetFieldType(column)))
                    return reader.GetByte(column);
                else if (typeof(bool).IsAssignableFrom(reader.GetFieldType(column)))
                    return (reader.GetBoolean(column) == true) ? (byte)1 : (byte)0;
                else return Marshal.ToByte(reader.GetString(column),def);
            }
            catch (Exception e)
            {
                logCastError(e, column);
                return (byte)0;
            }
        }


        public DateTime GetDateTime(int column)
        {
            try
            {
                return reader.GetDateTime(column);
            }
            catch (Exception e)
            {
                logCastError(e, column);
                return DateTime.MinValue;
            }
        }

        public decimal GetDecimal(int column)
        {
            try
            {
                return reader.GetDecimal(column);
            }
            catch (Exception e)
            {
                logCastError(e, column);
                return (decimal)0;
            }
        }

        public long GetInt64(int column)
        {
            try
            {
                if (reader.GetFieldType(column) == typeof(short))
                    return reader.GetInt16(column);
                else if (reader.GetFieldType(column) == typeof(int))
                    return reader.GetInt32(column);

                return reader.GetInt64(column);
            }
            catch (Exception e)
            {
                logCastError(e, column);
                return (long)0;
            }
        }

        public int GetInt32(int column)
        {
            try
            {
                if (reader.GetFieldType(column) == typeof(short))
                    return reader.GetInt16(column);

                return reader.GetInt32(column);
            }
            catch (Exception e)
            {
                logCastError(e, column);
                return 0;
            }
        }

        public short GetInt16(int column)
        {
            try
            {
                return reader.GetInt16(column);
            }
            catch (Exception e)
            {
                logCastError(e, column);
                return (short)0;
            }
        }

        public float GetFloat(int column)
        {
            try
            {
                return reader.GetFloat(column);
            }
            catch (Exception e)
            {
                logCastError(e, column);
                return (float)0;
            }
        }

        public double GetDouble(int column)
        {
            try
            {
                return reader.GetDouble(column);
            }
            catch (Exception e)
            {
                logCastError(e, column);
                return (double)0;
            }
        }

        public string GetString(int column)
        {
            try
            {
                return reader.GetString(column);
            }
            catch (Exception e)
            {
                logCastError(e, column);
                return "";
            }
        }

        public bool HasRows {
            get
            {
                return reader.HasRows;
            }
        }
    }
}
